<html>
<head>
<title>魔兽世界 WLK 80级 破甲计算器</title>
<script type="text/javascript">
function parseDouble(str, precision) {
    if (precision >= 0)
        return parseFloat(str).toFixed(precision);
    else
        return parseInt(str, 10);
}

// See: https://blog.csdn.net/qq_27194467/article/details/69943896
function parseDoubleEx(str, precision) {
    var num = str;
    num += '';
    num = num.replace(/[^0-9|\.]/g, '');
    if (/^0+/)
        num = num.replace(/^0+/, '');
    if (!/\./.test(num))
        num += '.00';
    if (/^\./.test(num))
        num = '0' + num;
    if (precision >= 0) {
        num += '00';
        num = num.match(/\d+\.\d{2}/)[0];
        return num.toFixed(precision);
    } else {
        num = num.match(/\d+/)[0];
        return parseInt(num, 10);
    }
}

function getArmorConstant(playerLevel) {
    var armorConstant;
    if (playerLevel >= 60)
        armorConstant = 400 + 85 * playerLevel + 4.5 * 85 * (playerLevel - 59);
    else
        armorConstant = 400 + 85 * playerLevel;
    return armorConstant;
}

// WLK 83级 BOSS护甲数值
var kLvl83BossArmor = 10643;

// 80级护甲常数
var kArmorConstant = 15232.5;  // == getArmorConstant(80)

// 破甲水晶 (固定200数值)
var kFixedCrystalPenetration = 200.0;

function getInnerPenetration(targetArmor, innerPenetrationPct) {
    targetArmor = parseFloat(targetArmor)
    innerPenetrationPct = parseFloat(innerPenetrationPct);
    var innerPenetrationLimit = (targetArmor + kArmorConstant) / 3.0;
    var innerPenetrationReal1 = (innerPenetrationPct / 100.0) * innerPenetrationLimit;
    var innerPenetrationReal2 = (innerPenetrationPct / 100.0) * targetArmor;
    var innerPenetrationReal = (innerPenetrationReal1 <= innerPenetrationReal2) ? innerPenetrationReal1 : innerPenetrationReal2;
    return innerPenetrationReal;
}

function getArmorReduction(targetArmor) {
    targetArmor = parseFloat(targetArmor);
    var armorReduction = ((targetArmor / (targetArmor + kArmorConstant)) * 100.0).toFixed(2);
    return parseFloat(armorReduction);
}

function CalcOuterPenetrationPct(targetArmor, ounterPenetrationPcts) {
    var penetrationPct = 1.0;
    var i;
    for (i = 0; i < ounterPenetrationPcts.length; i++) {
        var ounterPenetrationPct = ounterPenetrationPcts[i];
        penetrationPct = penetrationPct * (1.0 - ounterPenetrationPct / 100.0);
    }
    return (1.0 - penetrationPct);
}

function CalcRemainingArmor(targetArmor, innerPenetrationPct, ounterPenetrationPcts, useCrystal) {
    var outerPenetrationPct = CalcOuterPenetrationPct(targetArmor, ounterPenetrationPcts);
    if (useCrystal) {
        targetArmor -= kFixedCrystalPenetration;
    }
    var outerRemainingArmor = targetArmor * (1.0 - outerPenetrationPct);
    var outerPenetrationLimit = (outerRemainingArmor + kArmorConstant) / 3.0;

    var innerPenetrationReal = getInnerPenetration(outerRemainingArmor, innerPenetrationPct);
    if (innerPenetrationReal > outerPenetrationLimit)
        innerPenetrationReal = outerPenetrationLimit;
    var remainingArmor = outerRemainingArmor - innerPenetrationReal;
    return parseFloat(remainingArmor);
}

function CalcArmorReduction(targetArmor, innerPenetrationPct, ounterPenetrationPcts, useCrystal) {
    var remainingArmor = CalcRemainingArmor(targetArmor, innerPenetrationPct, ounterPenetrationPcts, useCrystal)
    var armorReduction = getArmorReduction(remainingArmor);
    return armorReduction;
}

function ResetResults() {
    var ctlTargetRemainingArmor = document.getElementById("TargetRemainingArmor");
    if (ctlTargetRemainingArmor != null) {
        ctlTargetRemainingArmor.value = "";
    }
    var ctlTargetArmorReduction = document.getElementById("TargetArmorReduction");
    if (ctlTargetArmorReduction != null) {
        ctlTargetArmorReduction.value = "0%";
    }
    var ctlTargetActualArmorReduction = document.getElementById("TargetActualArmorReduction");
    if (ctlTargetActualArmorReduction != null) {
        ctlTargetActualArmorReduction.value = "0%";
    }
}

function ParseTalentPenetrationPct() {
    var talentPenetrationPct = 0.0;
    var ctlInnerRogueHammerMaster = document.getElementById("InnerRogueHammerMaster");
    if (ctlInnerRogueHammerMaster != null) {
        if (ctlInnerRogueHammerMaster.checked) {
            talentPenetrationPct += 15.0;
            return talentPenetrationPct;
        }
    }
    var ctlInnerBloodDKTalentChuoXue = document.getElementById("InnerBloodDKTalentChuoXue");
    if (ctlInnerBloodDKTalentChuoXue != null) {
        if (ctlInnerBloodDKTalentChuoXue.checked) {
            talentPenetrationPct += 10.0;
            return talentPenetrationPct;
        }
    }
    var ctlInnerWarriorHammerMaster = document.getElementById("InnerWarriorHammerMaster");
    if (ctlInnerWarriorHammerMaster != null) {
        if (ctlInnerWarriorHammerMaster.checked) {
            talentPenetrationPct += 15.0;
        }
    }
    var ctlInnerWarriorBattleStance = document.getElementById("InnerWarriorBattleStance");
    if (ctlInnerWarriorBattleStance != null) {
        if (ctlInnerWarriorBattleStance.checked) {
            talentPenetrationPct += 10.0;
        }
    }
    return talentPenetrationPct;
}

function ParseOuterPenetrationPcts() {
    var outerPenetrationPcts = [];
    var ctlOuterArmorPenetration = document.getElementById("OuterArmorPenetration");
    if (ctlOuterArmorPenetration != null) {
        if (ctlOuterArmorPenetration.checked) {
            outerPenetrationPcts.push(20);
        }
    }
    var ctlOuterFaerieFire = document.getElementById("OuterFaerieFire");
    if (ctlOuterFaerieFire != null) {
        if (ctlOuterFaerieFire.checked) {
            outerPenetrationPcts.push(5);
        }
    }
    var ctlOuterShatteringThrow = document.getElementById("OuterShatteringThrow");
    if (ctlOuterShatteringThrow != null) {
        if (ctlOuterShatteringThrow.checked) {
            outerPenetrationPcts.push(20);
        }
    }
    return outerPenetrationPcts;
}

function ParseArmorPenetrationCrystal() {
    var useCrystal = false;
    var ctlOuterArmorPenetrationCrystal= document.getElementById("OuterArmorPenetrationCrystal");
    if (ctlOuterArmorPenetrationCrystal != null) {
        if (ctlOuterArmorPenetrationCrystal.checked) {
            useCrystal = true;
        }
    }
    return useCrystal;
}

function RecalcAll() {
    var ctlTargetArmor = document.getElementById("TargetArmor");
    if (ctlTargetArmor == null) {
        alert("[目标护甲值] TargetArmor 不存在!");
        return;
    }
    var targetArmor = parseInt(ctlTargetArmor.value, 10);
    if (isNaN(targetArmor)) {
        ResetResults();
        alert("[目标护甲值] 输入格式错误: " + ctlTargetArmor.value);
        return;
    }

    var ctlInnerPenetrationPct = document.getElementById("PlayerArmorPenetrationPercent");
    if (ctlInnerPenetrationPct == null) {
        alert("[玩家护甲穿透%] PlayerArmorPenetrationPercent 不存在!");
        return;
    }
    var innerPenetrationPct = parseFloat(parseFloat(ctlInnerPenetrationPct.value).toFixed(2));
    if (isNaN(innerPenetrationPct)) {
        ResetResults();
        alert("[玩家护甲穿透%] 输入格式错误: " + ctlInnerPenetrationPct.value);
        return;
    }

    var talentPenetrationPct = ParseTalentPenetrationPct();
    var ctlPlayerTalentArmorPenetrationPercent = document.getElementById("PlayerTalentArmorPenetrationPercent");
    if (ctlPlayerTalentArmorPenetrationPercent != null) {
        ctlPlayerTalentArmorPenetrationPercent.value = talentPenetrationPct + "%";
    }

    innerPenetrationPct += talentPenetrationPct;
    if (innerPenetrationPct > 100.0)
        innerPenetrationPct = 100.0;

    var outerPenetrationPcts = ParseOuterPenetrationPcts();
    var useCrystal = ParseArmorPenetrationCrystal();

    var targetOriginalArmorReduction = getArmorReduction(targetArmor);
    var targetRemainingArmor = CalcRemainingArmor(targetArmor, innerPenetrationPct, outerPenetrationPcts, useCrystal);
    var targetArmorReduction = getArmorReduction(targetRemainingArmor);

    var ctlTargetOriginalArmorReduction = document.getElementById("TargetOriginalArmorReduction");
    if (ctlTargetOriginalArmorReduction != null) {
        ctlTargetOriginalArmorReduction.value = targetOriginalArmorReduction + "%";
    }
    var ctlTargetRemainingArmor = document.getElementById("TargetRemainingArmor");
    if (ctlTargetRemainingArmor != null) {
        ctlTargetRemainingArmor.value = targetRemainingArmor.toFixed(2);
    }
    var ctlTargetArmorReduction = document.getElementById("TargetArmorReduction");
    if (ctlTargetArmorReduction != null) {
        ctlTargetArmorReduction.value = targetArmorReduction + "%";
    }

    var actualArmorReduction = (targetOriginalArmorReduction - targetArmorReduction).toFixed(2);

    var ctlTargetActualArmorReduction = document.getElementById("TargetActualArmorReduction");
    if (ctlTargetActualArmorReduction != null) {
        ctlTargetActualArmorReduction.value = actualArmorReduction + "%";
    }
}

function OnPlayerArmorPenetrationChange(ctl) {
    var ArmorPenetration = parseDouble(ctl.value, 0);
    if (isNaN(ArmorPenetration)) {
        ResetResults();
        //alert("ArmorPenetration = " + ArmorPenetration);
    } else {
        if (ArmorPenetration > 1400.0)
            ArmorPenetration = 1400.0;
        ctl.value = ArmorPenetration;
        var ArmorPenetrationPercent = (ArmorPenetration / 13.996).toFixed(2);
        if (ArmorPenetrationPercent > 100.0)
            ArmorPenetrationPercent = 100.0;
        var ctlPlayerArmorPenetrationPercent = document.getElementById("PlayerArmorPenetrationPercent");
        ctlPlayerArmorPenetrationPercent.value = ArmorPenetrationPercent + "%";
        RecalcAll();
    }
}

function OnPlayerArmorPenetrationPercentChange(ctl) {
    var ArmorPenetrationPercent = parseDouble(ctl.value, 2);
    if (isNaN(ArmorPenetrationPercent)) {
        ResetResults();
        //alert("ArmorPenetrationPercent = " + ArmorPenetrationPercent);
    } else {
        if (ArmorPenetrationPercent > 100.0)
            ArmorPenetrationPercent = 100.0;
        ctl.value = ArmorPenetrationPercent + "%";
        var ArmorPenetration = (ArmorPenetrationPercent * 13.996).toFixed(0);
        if (ArmorPenetration > 1400.0)
            ArmorPenetration = 1400.0;
        var cltPlayerArmorPenetration = document.getElementById("PlayerArmorPenetration");
        cltPlayerArmorPenetration.value = ArmorPenetration;
        RecalcAll();
    }
}

function ClearOtherInnerCheckBoxs(type) {
    if (type != "InnerRogueHammerMaster") {
        var ctlInnerRogueHammerMaster = document.getElementById("InnerRogueHammerMaster");
        if (ctlInnerRogueHammerMaster != null) {
            ctlInnerRogueHammerMaster.checked = false;
        }
    }
    if (type != "InnerBloodDKTalentChuoXue") {
        var ctlInnerBloodDKTalentChuoXue = document.getElementById("InnerBloodDKTalentChuoXue");
        if (ctlInnerBloodDKTalentChuoXue != null) {
            ctlInnerBloodDKTalentChuoXue.checked = false;
        }
    }
    if (type != "InnerWarriorHammerMaster" && type != "InnerWarriorBattleStance") {
        var ctlInnerWarriorHammerMaster = document.getElementById("InnerWarriorHammerMaster");
        if (ctlInnerWarriorHammerMaster != null) {
            ctlInnerWarriorHammerMaster.checked = false;
        }
    }
    if (type != "InnerWarriorHammerMaster" && type != "InnerWarriorBattleStance") {
        var ctlInnerWarriorBattleStance = document.getElementById("InnerWarriorBattleStance");
        if (ctlInnerWarriorBattleStance != null) {
            ctlInnerWarriorBattleStance.checked = false;
        }
    }
}

function OnInnerCheckBoxClick(type) {
    ClearOtherInnerCheckBoxs(type);
    RecalcAll();
}
</script>
</head>
<body>
<div id="header">
</div>
<div id="content">
<table align="center" width="800">
<tr>
<td>
<p>【WLK 80级 破甲计算器】，参考：<a href="https://bbs.nga.cn/read.php?tid=34417308" target="_blank">NGA论坛 - WLK 超详细破甲解析--带你了解破甲的计算机制</a></p>
<table>
<tr>
<th>&nbsp;</th>
<th>&nbsp;</th>
</tr>
<tr>
<td>WLK护甲上限：</td><td>护甲穿透[上限值] = (目标护甲值 + C) / 3</td>
</tr>
<tr>
<td>护甲减伤(百分比%)：</td><td>护甲减伤% = 剩余护甲 / (剩余护甲 + C)</td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td>80级护甲常数(护甲常数C) ：</td><td><input type="text" id="ArmorConstant" size="7" maxlength="20" value="15232.5" readonly></input></td>
</tr>
<!--
<tr>
<td>WLK 83级 BOSS护甲数值：</td><td><input type="text" id="Lvl83BossArmor" size="7" maxlength="20" value="10643" readonly></input></td>
</tr>
-->
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td colspan="2">&gt;&gt; 内破 &lt;&lt;</td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td>(玩家)护甲穿透等级：</td><td><input type="text" id="PlayerArmorPenetration" size="7" maxlength="10" value="0" required oninput="OnPlayerArmorPenetrationChange(this)"></input></td>
</tr>
<tr>
<td>(玩家)护甲穿透%：</td><td><input type="text" id="PlayerArmorPenetrationPercent" size="7" maxlength="10" value="0%" required onchange="OnPlayerArmorPenetrationPercentChange(this)"></input> &lt;-- 如手动修改此项请按回车</td>
</tr>
<tr>
<td>(玩家)额外护甲穿透%：</td><td><input type="text" id="PlayerTalentArmorPenetrationPercent" size="7" maxlength="10" value="0%" readonly></input></td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td valign="top"><input type="checkbox" id="InnerRogueHammerMaster" value="1" onclick="OnInnerCheckBoxClick('InnerRogueHammerMaster')"></input>&nbsp;<label for="InnerRogueHammerMaster">战斗贼-锤专精（15%破甲）</label><br />
<input type="checkbox" id="InnerBloodDKTalentChuoXue" value="1" onclick="OnInnerCheckBoxClick('InnerBloodDKTalentChuoXue')"></input>&nbsp;<label for="InnerBloodDKTalentChuoXue">血DK-天赋-[啜血]（10%破甲）</label>
</td>
<td valign="top"><input type="checkbox" id="InnerWarriorHammerMaster" value="2" onclick="OnInnerCheckBoxClick('InnerWarriorHammerMaster')"></input>&nbsp;<label for="InnerWarriorHammerMaster">武器战-锤专精（15%破甲）</label><br />
<input type="checkbox" id="InnerWarriorBattleStance" value="3" onclick="OnInnerCheckBoxClick('InnerWarriorBattleStance')"></input>&nbsp;<label for="InnerWarriorBattleStance">战士-战斗姿态（10%破甲）</label>
</td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td colspan="2">&gt;&gt; 外破 &lt;&lt;</td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td><input type="checkbox" id="OuterArmorPenetration" checked required onclick="RecalcAll()"></input>&nbsp;<label for="OuterArmorPenetration">(战士, 盗贼)破甲 (20%)</label></td><td><input type="checkbox" id="OuterArmorPenetrationCrystal" required onclick="RecalcAll()"></input>&nbsp;<label for="OuterArmorPenetrationCrystal">破甲水晶 (固定200数值)</label></td>
</tr>
<tr>
<td><input type="checkbox" id="OuterFaerieFire" checked required onclick="RecalcAll()"></input>&nbsp;<label for="OuterFaerieFire">精灵火, 虚弱诅咒 (5%)</label></td>
<td><input type="checkbox" id="OuterShatteringThrow" required onclick="RecalcAll()"></input>&nbsp;<label for="OuterShatteringThrow">(战士)碎裂投掷 (10秒 20%破甲)</label></td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td>(目标)等级：</td><td><input type="text" id="TargetLevel" size="3" maxlength="10" value="83" required></input></td>
</tr>
<tr>
<td>(目标)原始护甲值：</td><td><input type="text" id="TargetArmor" size="9" maxlength="16" value="10643" required></input></td>
</tr>
<tr>
<td>(目标)原始护甲减免%：</td><td><input type="text" id="TargetOriginalArmorReduction" size="9" maxlength="16" value="" readonly></input></td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td>(目标)剩余护甲值：</td><td><input type="text" id="TargetRemainingArmor" size="9" maxlength="16" value="" readonly></input></td>
</tr>
<tr>
<td>(目标)最终护甲减免%：</td><td><input type="text" id="TargetArmorReduction" size="9" maxlength="16" value="" readonly></input></td>
</tr>
<tr>
<td>&nbsp;</td><td>&nbsp;</td>
</tr>
<tr>
<td><font color="#F03344">实际有效护甲减免%：</font></td><td><input type="text" id="TargetActualArmorReduction" size="9" maxlength="16" value="" style="color: #F03344;" readonly></input></td>
</tr>
<tr>
</table>
</td>
</tr>
</table>
</div>
<div id="footer">
</div>
<script type="text/javascript">
RecalcAll();
</script>
</body>
</html>